"use strict";

import * as serialport from 'serialport';
import * as q from 'q';
import {BaseCom} from './../BaseCom';

/** 操作全站仪的com类 */
class GeoCom extends BaseCom {
    
    /** 
     * @type {string}
     * 串口数据的终止符,\r\n
     */
    public linebreak = "\r\n";

    /** 
     * GeoCom的构造函数,立马打开COM口
     * @param {string} portName -  com口的名称
     */
    constructor(portName?: string) {
        super(portName);
    }
    
    
    /** 
   * 工厂方法
   * 生产GeoCom数据的处理函数,用来代替父类中的_proccessData函数
   * 对于不同的指令,都可能有不同的操作函数,在这里分为两类:
   * 第一类是有返回值的指令处理,传入返回值的构造函数,在处理的时候构造一个返回值,resolve null表示失败.
   * 注意:返回值类构造函数的属性定义必须按照串口返回数据的顺序来进行
   * 第二类是没有返回值的指令处理,传入null,只需要判断是否成功即可,成功resolve true,失败resolve false
   * @param {Constructor} ResultType 传入对象的构造函数
   * @return {Function} 处理函数
   */
    private _dataProcessFactory(ResultType: any): void {
        if (ResultType) {
            /** 对于GeoCom中有返回数据的部分做处理  */
            this._proccessData = () => {
                let str = this._tempBuffer.toString();
                let regStr = "%R1P,0,0:0,";
                let reg = new RegExp(regStr);
                if (reg.test(str)) {
                    let valueArray = str.slice(regStr.length).split(',');
                    let resultData = new ResultType();
                    let keys = Object.keys(resultData);
                    if (valueArray.length < keys.length) {
                        //获取的数据长度不够赋值到每个属性,那肯定是结果有错误,直接null
                        this._defer.resolve(null);
                        return;
                    } else {
                        for (let key of keys) {
                            let index = keys.indexOf(key);
                            resultData[key] = parseFloat(valueArray[index]);
                        }
                        this._defer.resolve(resultData);
                    }
                } else {
                    this._defer.resolve(null);
                }
            }
        } else {
            /** 对于GeoCom中只是指示成功与失败的返回数据处理  */
            this._proccessData = () => {
                let str = this._tempBuffer.toString();
                if (str === '%R1P,0,0:0') {
                    this._defer.resolve(true);
                } else {
                    this._defer.resolve(false);
                }
            }
        }
    }
    
    /** 
     * 判断本次串口数据读取是否结束的函数
     * 全站仪返回的最终数据为\r\n的时候判定本次读取结束 
     */
    protected _dataComplete = (): boolean => {
        let str = this._tempBuffer.toString();
        if (str.length < 2) return false; 
        let suffix = str.slice(str.length - 2);
        if (suffix === this.linebreak) {
            //最后截掉终止符
            this._tempBuffer = this._tempBuffer.slice(0, this._tempBuffer.length - this.linebreak.length);
            return true;
        }
        else return false;
    }
    
    /** 
     * 打开或关闭ATR 
     * @param {string|number}  toggle - 打开或关闭ATR的参数,'1'或'open'为打开,'0'或'close'为关闭
     * @return {Promise} 返回Promise,成功resolve true,失败resolve false 或 null;
     */
    public toggleATR(toggle: string | number): q.Promise<boolean> {
        if (toggle === 'open') toggle = '1';
        if (toggle === 'close') toggle = '0';
        this._preSurvey();
        this.port.write(`%R1Q,18005:${toggle}${this.linebreak}`);
        //3秒超时
        this._surveyTimeout(3000);
        //替换数据处理函数
        this._dataProcessFactory(null);
        return this._defer.promise;
    }
  
    
    /** 
    * 打开或关闭激光
    * @param {string|number}  toggle - 打开或关闭激光的参数,'1'或'open'为打开,'0'或'close'为关闭
    * @return {Promise} 返回Promise,成功resolve true,失败resolve false 或 null;
    */
    public toggleLaser(toggle: number | string): q.Promise<boolean> {
        if (toggle === 'open') toggle = '1';
        if (toggle === 'close') toggle = '0';
        this._preSurvey();
        this.port.write(`%R1Q,1004:${toggle}${this.linebreak}`);
        //设置超时
        this._surveyTimeout(5000);
        //替换数据处理函数
        this._dataProcessFactory(null);
        return this._defer.promise;
    }

    /** 
     * 向目前的照准方向执行测量操作,前提是全站仪已经对准了棱镜或指定的位置
     * @return {Promise<MeasureResult>} 最终返回测量的结果
     */
    public measure(): q.Promise<MeasureResult> {
        this._preSurvey();
        this.port.write("%R1Q,17017:2\r\n");
        this._surveyTimeout(8000);
        this._dataProcessFactory(MeasureResult);
        return this._defer.promise;
    }

    /** 
     * 转动全站仪到指定的位置,默认启用ATR
     * @param {number} hz 指定的水平角,单位弧度
     * @param {number} hv 指定的垂直角,单位弧度
     * @param {boolean} [enableATR=true] 是否使用ATR瞄准,默认采用ATR.不采用时,全站仪仅仅是转向到指定的方位,而不精确对准棱镜
     */
    public makePosition(hz: number, hv: number, enableATR?: boolean): q.Promise<boolean> {
        let ATR = typeof (enableATR) === 'undefined' ? 1 : (enableATR ? 1 : 0);
        this._preSurvey();
        this.port.write(`%R1Q,9027:${hz},${hv},0,${ATR},0\r\n`);
        this._surveyTimeout(6000);
        this._dataProcessFactory(null);
        return this._defer.promise;
    }


    /** 
     * 切换测量模式,测量模式有两种,有棱镜和无棱镜
     * @param {string} method 要切换的测量方式,RL代表无棱镜,IR代表有棱镜
     */
    public toggleMeaureMethod(method: MeasureMethod | string): q.Promise<boolean> {
        if (typeof method === 'string') {
            if (method.toString().toUpperCase() == "RL") {
                method = "3";
            }
            else if (method.toString().toUpperCase() == "IR") {
                method = "0";
            }
        }
        this._preSurvey();
        this.port.write(`%R1Q,17019:${method}\r\n`);
        this._surveyTimeout(5000);
        this._dataProcessFactory(null);
        return this._defer.promise;
    }

    
    /** 
     * 获取测站坐标
     * @return {object} 测站坐标
     * @property {number} e y坐标
     * @property {number} n x坐标
     * @property {number} h z坐标
     * @property {number} hi 仪器高
     */
    public getStationCoor(): q.Promise<Coor> {
        this._preSurvey();
        this.port.write(`%R1Q,2009:\r\n`);
        this._surveyTimeout(3000);
        this._dataProcessFactory(Coor);
        return this._defer.promise;
    }


    /** 
     * 是否串口已经正确的连接到全站仪
     * 该方法调用getStationCoor方法来判断是否连接到全站仪
     * 如果能正确获取到测站坐标,说明全站仪连接正确
     * @return {boolean} true为连接,false为失败
     */
    public isTpsConnected(): q.Promise<boolean> {
        let defer: q.Deferred<any> = q.defer();
        this.getStationCoor()
            .then(function(coor) {
                if (coor != null && coor.e != null) {
                    defer.resolve(true);
                } else {
                    defer.resolve(false);
                }
            });
        return defer.promise;
    }


}

class MeasureResult {
    hz: number = null;
    hv: number = null;
    slope: number = null;
}


class Coor {
    e: number = null;
    n: number = null;
    h: number = null;
    hi: number = null;
}

enum MeasureMethod {
    IR = 0,  // IR Standard 有棱镜
    RL = 3 // RL Standard  无棱镜
};

export {GeoCom, MeasureResult, Coor, MeasureMethod};